package com.macro.cloud.hystrixservice.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.macro.cloud.hystrixservice.domain.CommonResult;
import com.macro.cloud.hystrixservice.domain.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

@Service
public class UserService {
    private Logger LOGGER = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private RestTemplate restTemplate;
    @Value("${service-url.user-service}")
    private String userServiceUrl;

    /**
     * 根据id查询用户的信息
     * 在UserService中添加调用方法与服务降级的方法
     * @param id
     * @return
     */
    @HystrixCommand(fallbackMethod = "getDefaultUser" )
    public CommonResult getUser(Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}",CommonResult.class,id);
    }

    public CommonResult getDefaultUser(@PathVariable Long id) {
        User defaultUser = new User(-1L,"defaultUser","123456");
        return new CommonResult(defaultUser);
    }

    /**
     * 设置命令、分组以及线程池
     */
    @HystrixCommand(fallbackMethod = "getDefaultUser",
            commandKey = "getUserCommand",
            groupKey = "getUserGroup",
            threadPoolKey = "getUserThreadPool")
    public CommonResult getUserCommand(@PathVariable Long id) {
        return restTemplate.getForObject(userServiceUrl + "/user/{1}",CommonResult.class,id);
    }

    /**
     * 忽略异常
     * 在UserService中添加方法，可以忽略NullPointerException。
     * 当id为1的时候抛出IndexOutOfBoundsException,当id为2的时候可以抛出NullPointerException
     */
    @HystrixCommand(fallbackMethod = "getDefaultUser2",ignoreExceptions = {NullPointerException.class})
    public CommonResult getUserException(Long id) {
        if(id == 1) {
            throw new IndexOutOfBoundsException();
        }else if(id == 2) {
            throw new NullPointerException();
        }
        return restTemplate.getForObject(userServiceUrl + "/user/{1}",CommonResult.class,id);
    }

    public CommonResult getDefaultUser2(@PathVariable Long id,Throwable e) {
        LOGGER.error("getDefaultUser2 id:{},throwable class:{}",id,e.getClass());
        User defaultUser = new User(-2L,"defaultUser2","123456");
        return new CommonResult(defaultUser);
    }

    @CacheResult(cacheKeyMethod = "getCacheKey")
    @HystrixCommand(fallbackMethod = "getDefaultUser",commandKey = "getUserCache")
    public CommonResult getUserCache(Long id) {
        LOGGER.info("getUserCache id:{}",id);
        return restTemplate.getForObject(userServiceUrl + "/user/{1}",CommonResult.class,id);
    }

    /**
     * 为缓存生成key的方法
     * @param id
     * @return
     */
    public String getCacheKey(Long id) {
        return String.valueOf(id);
    }

    /**
     * AsyncResult 类中的 invoke() 方法用于执行异步操作的回调函数。当异步操作完成后，可以通过调用 invoke()
     * 方法来触发回调函数的执行，将异步操作的结果传递给回调函数进行处理。
     *
     * 具体来说，invoke() 方法会执行与 AsyncResult 实例关联的回调函数，并将异步操作的结果作为参数传递给回调函数。
     * 这样，你可以在回调函数中处理异步操作的结果，进行相应的逻辑处理或者后续操作。
     *
     * 需要注意的是，invoke() 方法的调用通常是在异步操作完成后进行的，以确保在回调函数中处理的是最终的异步操作结果。
     *
     * 总结起来，AsyncResult 类中的 invoke() 方法用于执行异步操作的回调函数，并将异步操作的结果传递给回调函数进行处理。
     * 这样可以实现在异步操作完成后，对结果进行相应的处理逻辑。
     *
     * @param id
     * @return
     */
    @HystrixCollapser(batchMethod = "getUserByIds",collapserProperties = {
         @HystrixProperty(name = "timerDelayInMilliseconds",value = "100")
    })
    public Future<User> getUserFuture(Long id){
        return new AsyncResult<User>() {
            @Override
            public User invoke() {
                CommonResult commonResult = restTemplate.getForObject(userServiceUrl + "/user/{1}"
                        ,CommonResult.class,id);
                User user = (User) commonResult.getData();
                LOGGER.info("getUserById username:{}", user.getUsername());
                return user;
            }
        };
    }

    /**
     * CollUtil.join(ids, ",")是一个使用CollUtil工具类的方法，用于将一个集合中的元素按照指定的分隔符连接成一个字符串。
     * 其中，ids是一个集合，","是指定的分隔符。这个方法的作用是将集合中的元素按照指定的分隔符连接起来，形成一个字符串。
     * 例如，如果ids是一个包含[1, 2, 3]的集合，那么调用CollUtil.join(ids, ",")的结果就是"1,2,3"。
     * @param ids
     * @return
     */
    @HystrixCommand
    public List<User> getUserByIds(List<Long> ids) {
        LOGGER.info("getUserByIds:{}",ids);
        CommonResult commonResult = restTemplate.getForObject(userServiceUrl +"/user/getUserByIds?ids={1}"
                ,CommonResult.class,CollUtil.join(ids,","));
        return (List<User>) commonResult.getData();
    }

}
